#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "CServer_UDP.h"
#include <time.h>
#include <iostream>
 
#pragma comment(lib, "ws2_32.lib")
#pragma execution_character_set("utf-8")//utf-8编码文件
 
//服务器 
CServer_UDP::CServer_UDP():m_terminal(false)
{
	data_size = 0;
	//capacity = MAX_size;
	//open_file_flag = false;
	//必须进行如下初始化， 否则socket()会返回10093错误
	//初始化WSA  
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0) //通过一个进程初始化ws2_32.dll
	{
		std::cout << "Initialize WSA failed" << std::endl;
		return;
	}
	//初始化UDDP套接字
	m_sServer = socket(AF_INET, SOCK_DGRAM, 0);
 
	struct sockaddr_in m_SocAddrserver;
	/*
	sin_family:地址族
	sin_port:16位TCP/UDP 端口号
	sin_addr:32位IP地址
	sockaddr_in 一般变量赋值后,强制类型转换后传入用scokaddr做参数的函数。
	sockaddr_in用于socket定义和赋值；sockaddr用于函数参数
	*/
	m_SocAddrserver.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//0
	m_SocAddrserver.sin_family = AF_INET;
	m_SocAddrserver.sin_port = htons(8090);
 
	int ret = bind(m_sServer, (sockaddr*)&m_SocAddrserver, sizeof(m_SocAddrserver));
	if (ret == -1)
	{
		std::cout << "bind failed!" << std::endl;
		WSACleanup();
	}
	else
	{
		//此处必须赋初值，不然会导致服务器端无法正常发送
		int len_Client = sizeof(sockaddr);  
		char recBuf[1025];
		//todo:之后用Qt在界面中先输入接收端的配置信息。这里主要是为了,得到客服端的配置信息，为之后向客服端发消息做准备。
		int len = recvfrom(m_sServer, recBuf, 1024, 0, (sockaddr*)&m_SocAddrClient, &len_Client);
		if (len > 0)
		{
			//recBuf[len] = '\0';
			//std::cout << "client message:" << recBuf << std::endl;
			//std::cout << "get client config \n";
			std::cout << "connection established\n ";
		}
	}
 
 
}
//服务器只接收不发送
void CServer_UDP::SendMsg(const char sendBuf[])
{
	auto ret = sendto(m_sServer, sendBuf, strlen(sendBuf), 0, (sockaddr*)&m_SocAddrClient, sizeof(m_SocAddrClient));
	if (ret == -1)
	{
		std::cout << "send failed" << std::endl;
		std::cout << GetLastError()<< std::endl;
	}
}
void CServer_UDP::RecMsg()
{
	char recBuf[1025];
	fd_set fdRead;
	timeval timeout;
	timeout.tv_sec = 3;
	timeout.tv_usec = 0;
	while (!m_terminal)
	{
		/*设置UDP接收超时，目前为阻塞时接收，设置阻塞\超时时间，
		目前存在问题因为，如果文件一直打开在写数据，何时关闭文件？？？
		解决方法：1、传输前面的文件会因为存满字节数自动关闭
		2、设置超时时间，比如等待5s中，先判断文件是否关闭然后在关闭最后一个文件
		到时候实测可能存在的问题
		1、1024个缓冲区够用吗？
		2、存文件的速度能赶得上数据传输速度吗？
		*/
		FD_ZERO(&fdRead);
		FD_SET(m_sServer,&fdRead);
		int ret = select(0, &fdRead, NULL, NULL, &timeout);
		if (ret == 0 ) //超时
		{
			if (ofile.is_open()) {//超时 文件还处于打开状态 关闭文件
				ofile.close();
				data_size = 0;
				std::cout << "time out close file!\n";
			}
		}
		else if(ret==SOCKET_ERROR)//出错
		{
			std::cout << "SOCKET_ERROR error\n";
		}
		else if (ret > 0)
		{
			int len = recvfrom(m_sServer, recBuf, 1024, 0, 0, 0);
			if (!ofile.is_open())//还没打开文件
			{
				openFile();
			}
			if (data_size +len > MAX_size) //当前文件存不下读取的len长度的数据。
			{

				auto capacity = MAX_size - data_size;//当前文件的容量
				if (ofile.is_open()) {
					ofile.write(recBuf, capacity * sizeof(char));//将当前文件存满了
					ofile.close();
					int nowSize = len - capacity;//当前还有这么多数据要保存, 现在的数据长度很可能大于最大字节数量
					int i = 0;
					while (nowSize >= MAX_size)
					{
						openFile();//打开新的文件 把剩余的内容存在新的文件中
						if (ofile.is_open()) {

							ofile.write(recBuf + capacity+i* MAX_size, MAX_size * sizeof(char));
							ofile.close();
							i++;
							nowSize -= MAX_size;
						}
					}
					if (nowSize > 0)
					{
						openFile();//打开新的文件 把剩余的内容存在新的文件中
						if (ofile.is_open()) {

							ofile.write(recBuf + capacity + i * MAX_size, nowSize * sizeof(char));//还没存满不能关闭文件
							data_size = nowSize;
						}
					}
					else if(nowSize==0){

						data_size = 0;
					}
				}

			}
			else if (data_size + len <= MAX_size&&ofile.is_open())//还能存下直接将所有数据存入当前文件
			{
				ofile.write(recBuf, len* sizeof(char));
				data_size += len;
			}
			else {
				std::cout << "error\n";
			}
		}
		else {
			std::cout << "error222\n";
		}

	}
	
}
CServer_UDP::~CServer_UDP()
{
	closesocket(m_sServer);

	WSACleanup();
 
}

void CServer_UDP::getTimeToFileName(std::string & m_file_name)
{
	time_t timep;
	time(&timep);
	char tmp[125];
	tm tm1;
	localtime_s(&tm1, &timep);
	strftime(tmp, sizeof(tmp), "%Y-%m-%d_%H-%M-%S", &tm1);
	m_file_name = tmp;
	if (m_file_name == m_file_name_last) {//和上一次的文件名相同就添加后缀_1
		m_file_name += "_1";
	}
	m_file_name_last = m_file_name;
}

void CServer_UDP::openFile()
{
	//可以用c++ 的ofstream 也可用c的fopen
	//考虑到写文件的速度用c语言的API
	//ofile.open();
	//file = fopen("udp_data.txt", "w");//新写一个函数获取当地时间，用时间命名文件名 todo：之后修改为二进制保存，目前方便调试
	if (ofile.is_open()) {
		ofile.close();
	}
	getTimeToFileName(m_file_name);
	ofile.open(m_file_name + ".txt", std::ios::out);//文件文本输出，调试好以后更改为二进制输出
	std::cout <<std::endl<< m_file_name << std::endl;
	if (ofile.is_open()) std::cout << "open file sucessfully!" << std::endl;
}

void CServer_UDP::closeFile()
{
}
